/*
 * ============================================================================
 *
 *  [THC RPG] Total HardCore RPG
 *
 *  File:          adrenaline.inc
 *  Type:          Upgrade
 *  Description:   Gives you the ability to turn up your speed when you got hurt
 *  Author:        ArsiRC
 *
 *  Copyright (C) 2009-2011  ArsiRC
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * ============================================================================
 */

/**
 * This module's identifier.
 */
new Module:g_moduleAdren;

/**
 * Function for outside files to use to return the module's identifier.
 */
stock Module:Adren_GetIdentifier() { return g_moduleAdren; }

/**
 * Called when AdrenMenu is created.
 *
 * @param hMenu     Handle to AdrenMenu.
 */
new ProjectEvent:g_evOnAdrenMenuCreated;

/**
 * Called when AdrenMenu is deleted.
 *
 * @param hMenu     Handle to AdrenMenu.
 */
new ProjectEvent:g_evOnAdrenMenuDeleted;

/**
 * Handle to AdrenMenu.
 */
new Handle:g_hAdrenMenu;

/**
 * Adren's button index in RPGMenu.
 */
new g_iAdrenRPGMenuButton;

/**
 * Global variables
 */
new g_adrenUpgradeConfigCache[UpgradeConfigs];
new Handle:g_hPlayerIsAdrenalined[MAXPLAYERS+1] = {INVALID_HANDLE,...};

/**
 * Register this module.
 */
Adren_Register()
{
    // Define all the module's data as layed out by enum ModuleData in project.inc.
    new moduledata[ModuleData];
    
    moduledata[ModuleData_Disabled] = false;
    moduledata[ModuleData_Hidden] = false;
    strcopy(moduledata[ModuleData_ShortName], MM_DATA_SHORTNAME, "Adren");
    strcopy(moduledata[ModuleData_FullName], MM_DATA_FULLNAME, "Adrenaline");
    strcopy(moduledata[ModuleData_Description], MM_DATA_DESCRIPTION, "Gives you the ability to turn up your speed when you got hurt");

    new Module:dependencies[MM_DATA_DEPENDENCIES];
    dependencies[0] = Core_GetIdentifier();
    dependencies[1] = INVALID_MODULE;
    moduledata[ModuleData_Dependencies] = dependencies;
    
    new Module:interlocks[MM_DATA_INTERLOCKS];
    interlocks[0] = INVALID_MODULE;
    moduledata[ModuleData_Interlocks] = interlocks;
    
    moduledata[ModuleData_TeamLock] = 0;
    moduledata[ModuleData_MaxLevel] = 6;
    moduledata[ModuleData_Cost] = 10;
    moduledata[ModuleData_iCost] = 10;

    // Send this array of data to the module manager.
    g_moduleAdren = ModuleMgr_Register(moduledata);

    // Now register the events we're going to use.
    #if defined EVENT_MANAGER
        EventMgr_RegisterEvent(g_moduleAdren, "Event_OnEventsRegister", "Adren_OnEventsRegister");
    #endif

    // Create custom events.
    g_evOnAdrenMenuCreated = EventMgr_CreateEvent("Event_OnAdrenMenuCreated");
    g_evOnAdrenMenuDeleted = EventMgr_CreateEvent("Event_OnAdrenMenuDeleted");

    // Register config file(s) that this module will use.
    #if defined CONFIG_MANAGER
        ConfigMgr_Register(g_moduleAdren, "Adren_OnConfigReload", "configs/thc_rpg/adrenaline.txt");
    #endif
}

/**
 * Register all events here.
 */
public Adren_OnEventsRegister()
{
    // Register all the events needed for this module.
    EventMgr_RegisterEvent(g_moduleAdren, "Event_OnAllModulesLoaded",           "Adren_OnAllModulesLoaded");
    EventMgr_RegisterEvent(g_moduleAdren, "Event_OnMyModuleEnable",             "Adren_OnMyModuleEnable");
    EventMgr_RegisterEvent(g_moduleAdren, "Event_OnMyModuleDisable",            "Adren_OnMyModuleDisable");
    EventMgr_RegisterEvent(g_moduleAdren, "Event_OnRPGMenuCreated",             "Adren_OnRPGMenuCreated");
    EventMgr_RegisterEvent(g_moduleAdren, "Event_OnMapStart",                   "Adren_OnMapStart");
    EventMgr_RegisterEvent(g_moduleAdren, "Event_OnClientDisconnect",           "Adren_OnClientDisconnect");
    #if defined PROJECT_GAME_CSS
        EventMgr_RegisterEvent(g_moduleAdren, "Event_PlayerSpawn",              "Adren_PlayerSpawn");
        EventMgr_RegisterEvent(g_moduleAdren, "Event_PlayerHurt",               "Adren_PlayerHurt");
        EventMgr_RegisterEvent(g_moduleAdren, "Event_PlayerDeath",              "Adren_PlayerDeath");
    #endif

    // Custom events
    EventMgr_RegisterEvent(g_moduleAdren, "Event_OnUpgradePurchase",            "Adren_OnUpgradePurchase");
    EventMgr_RegisterEvent(g_moduleAdren, "Event_OnUpgradeSell",                "Adren_OnUpgradeSell");
}

/**
 * All modules and events have been registered by this point.  Event priority can be changed here.
 */
public Adren_OnEventsReady()
{
}

/**
 * Plugin is loading.
 */
Adren_OnPluginStart()
{
    // Register the module.
    Adren_Register();
}

#if defined CONFIG_MANAGER
/**
 * Called when a registered config file (by this module) is manually reloaded.
 */
public Adren_OnConfigReload(configindex)
{
    #if defined LOG_MANAGER
        LogMgr_Print(g_moduleAdren, LogType_Normal, "AdrenConfigReload", "Reloaded speed module's config (index %d)", configindex);
    #endif

    ConfigMgr_CacheKv(g_moduleAdren, CM_CONFIGINDEX_FIRST, "adrenModule_ConfigCache");
}

/**
 * Read config values
 */
public KvCache:adrenModule_ConfigCache(Handle:kv, sectionindex, const String:sectionname[])
{
    // Read Upgrade config
    if(StrEqual(sectionname, "adrenaline"))
    {
        g_adrenUpgradeConfigCache[UpgradeConfig_Disable] = KvGetNum(kv, "disable");
        g_adrenUpgradeConfigCache[UpgradeConfig_TeamLock] = KvGetNum(kv, "teamlock");
        g_adrenUpgradeConfigCache[UpgradeConfig_MaxLevel] = KvGetNum(kv, "maxlevel");
        g_adrenUpgradeConfigCache[UpgradeConfig_Cost] = KvGetNum(kv, "cost");
        g_adrenUpgradeConfigCache[UpgradeConfig_iCost] = KvGetNum(kv, "icost");
        g_adrenUpgradeConfigCache[UpgradeConfig_Percent] = KvGetFloat(kv, "percent");
        g_adrenUpgradeConfigCache[UpgradeConfig_Interval] = KvGetFloat(kv, "interval");

        if(g_adrenUpgradeConfigCache[UpgradeConfig_Disable]==1)
            ModuleMgr_Disable(g_moduleAdren);

        ModuleMgr_WriteCell(g_moduleAdren, ModuleData_TeamLock, g_adrenUpgradeConfigCache[UpgradeConfig_TeamLock]);
        ModuleMgr_WriteCell(g_moduleAdren, ModuleData_MaxLevel, g_adrenUpgradeConfigCache[UpgradeConfig_MaxLevel]);
        ModuleMgr_WriteCell(g_moduleAdren, ModuleData_Cost, g_adrenUpgradeConfigCache[UpgradeConfig_Cost]);
        ModuleMgr_WriteCell(g_moduleAdren, ModuleData_iCost, g_adrenUpgradeConfigCache[UpgradeConfig_iCost]);
    }
    else
        ModuleMgr_Disable(g_moduleAdren);
}
#endif

/**
 * All modules have been registered at this point.
 */
public Adren_OnAllModulesLoaded()
{
    Adren_CreateMenu();
}

/**
 * The module that hooked this event callback has been enabled.
 *
 * @param refusalmsg    The string that is printed if Plugin_Handled is returned and it is non-empty.
 * @param maxlen        The max length of the string.
 *
 * @return              Return Plugin_Handled to stop enable, and Plugin_Continue to allow it.
 */
public Action:Adren_OnMyModuleEnable(String:refusalmsg[], maxlen)
{
    // Enable Adren's button to the menu.
    new Handle:hRPGMenu = MenuLib_FindMenuById("RPGMenuUpgrades");
    MenuLib_BtnWriteCell(hRPGMenu, g_iAdrenRPGMenuButton, MenuBtn_Style, ITEMDRAW_DEFAULT);

    Adren_CreateMenu();

    new size=GetArraySize(g_hVecPlayers);
    for(new x=0;x<size;x++)
    {
        new Handle:vecPlayer=GetArrayCell(g_hVecPlayers,x);
        new client=GetArrayCell(vecPlayer,VECTOR_PLAYERS_CLIENT);
        if(Util_IsClientInGame(client))
            Adren_StopAdrenaline(client);
    }

    return Plugin_Continue;
}

/**
 * The module that hooked this event callback has been disabled.
 *
 * @param refusalmsg    The string that is printed if Plugin_Handled is returned and it is non-empty.
 * @param maxlen        The max length of the string.
 *
 * @return              Return Plugin_Handled to stop disable, and Plugin_Continue to allow it.
 */
public Action:Adren_OnMyModuleDisable(String:refusalmsg[], maxlen)
{
    // Disable Adrens's button to the menu.
    new Handle:hRPGMenu = MenuLib_FindMenuById("RPGMenuUpgrades");
    if (hRPGMenu != INVALID_HANDLE)
        MenuLib_BtnWriteCell(hRPGMenu, g_iAdrenRPGMenuButton, MenuBtn_Style, ITEMDRAW_DISABLED);

    Adren_DeleteMenu();
    
    new size=GetArraySize(g_hVecPlayers);
    for(new x=0;x<size;x++)
    {
        new Handle:vecPlayer=GetArrayCell(g_hVecPlayers,x);
        new client=GetArrayCell(vecPlayer,VECTOR_PLAYERS_CLIENT);
        if(Util_IsClientInGame(client))
            Adren_StopAdrenaline(client);
    }

    return Plugin_Continue;
}

Adren_CreateMenu()
{
    // Register menu with menulib.
    g_hAdrenMenu = MenuLib_CreateMenu("AdrenMenu", INVALID_FUNCTION, INVALID_FUNCTION, "Adren fullname", true, false, false);

    MenuLib_AddMenuBtnEx(g_hAdrenMenu, "Adren description", "", true, ITEMDRAW_DISABLED, INVALID_FUNCTION, BtnNextMenu_None, "");
    MenuLib_AddMenuBtnEx(g_hAdrenMenu, "RPGMenu purchase", "", true, ITEMDRAW_DEFAULT, Adren_AdrenMenuPurchaseBtn, BtnNextMenu_None, "");
    MenuLib_AddMenuBtnEx(g_hAdrenMenu, "RPGMenu sell", "", true, ITEMDRAW_DEFAULT, Adren_AdrenMenuSellBtn, BtnNextMenu_None, "");

    // Forward event to all modules.
    new any:eventdata[1][1];
    eventdata[0][0] = g_hAdrenMenu;
    EventMgr_Forward(g_evOnRPGMenuCreated, eventdata, sizeof(eventdata), sizeof(eventdata[]), g_CommonDataType2);
}

Adren_DeleteMenu()
{
    // Forward event to all modules.
    new any:eventdata[1][1];
    eventdata[0][0] = g_hAdrenMenu;
    EventMgr_Forward(g_evOnAdrenMenuDeleted, eventdata, sizeof(eventdata), sizeof(eventdata[]), g_CommonDataType2);

    // Delete the entire menu tree.
    MenuLib_DeleteMenu(g_hAdrenMenu);
}

/**
 * Called when RPGMenu is created.
 *
 * @param hMenu     Handle to Menu.
 */
public Adren_OnRPGMenuCreated(Handle:hMenu)
{
    Adren_AddButtonToRPGMenu(hMenu);
}

/**
 * The map has started.
 */
public Adren_OnMapStart()
{
    #if defined CONFIG_MANAGER
        ConfigMgr_CacheKv(g_moduleAdren, CM_CONFIGINDEX_FIRST, "adrenModule_ConfigCache");
    #endif
}

/**
 * Client is disconnecting from the server.
 *
 * @param client    The client index.
 */
public Adren_OnClientDisconnect(client)
{
    Adren_StopAdrenaline(client);
}

/**
 * Client has spawned.
 *
 * @param client    The client index.
 *
 */
public Adren_PlayerSpawn(client)
{
    Adren_StopAdrenaline(client);
}

/**
 * Client has been damaged.
 *
 * @param victim        The index of the hurt client.
 * @param attacker      The index of the attacking client.
 * @param health        How much health the client has after the damage.
 * @param armor         How much armor the client has after the mirrordmg.
 * @param weapon        The weapon classname used to hurt the victim. (No weapon_ prefix)
 * @param dmg_health    The amount of health the victim lost.
 * @param dmg_armor     The amount of armor the victim lost.
 * @param hitgroup      The hitgroup index of the victim that was damaged.
 */
public Adren_PlayerHurt(victim, attacker, health, armor, const String:weapon[], dmg_health, dmg_armor, hitgroup)
{
    if(AreValidPlayers(victim,attacker)&&g_hPlayerIsAdrenalined[victim]==INVALID_HANDLE)
        if(AreValidTeams(victim,attacker,g_CoreConfigCache[CoreConfig_freeforall],g_adrenUpgradeConfigCache[UpgradeConfig_TeamLock]))
        {
            new level=GetPlayerUpgradeLevel(victim,g_moduleAdren);
            if(level>0)
            {
                new Float:amount=GetModifiedSpeed(victim)+(level*g_adrenUpgradeConfigCache[UpgradeConfig_Percent]);
                OffsLib_SetClientLMV(victim, amount);
                g_hPlayerIsAdrenalined[victim]=CreateTimer(g_adrenUpgradeConfigCache[UpgradeConfig_Interval],Timer_OnAdrenalineStop,victim,TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE);
            }
        }
}

/**
 * Client has been killed.
 *
 * @param victim    The index of the killed client.
 * @param attacker  The killer of the victim.
 * @param weapon    The weapon classname used to kill the victim. (No weapon_ prefix)
 * @param headshot  True if the death was by headshot, false if not.
 */
public Adren_PlayerDeath(victim, attacker, const String:weapon[], bool:headshot)
{
    if(Util_IsClientInGame(victim))
        Adren_StopAdrenaline(victim);
}

/**
* Client has purchased an Upgrade
*/
public Adren_OnUpgradePurchase(client, level)
{
    if(Util_IsClientInGame(client))
        Adren_StopAdrenaline(client);
}

/**
* Client has sold an Upgrade
*/
public Adren_OnUpgradeSell(client, level)
{
    if(Util_IsClientInGame(client))
        Adren_StopAdrenaline(client);
}

public Action:Timer_OnAdrenalineStop(Handle:timer,any:client)
{
    OffsLib_SetClientLMV(client, GetModifiedSpeed(client));
    g_hPlayerIsAdrenalined[client]=INVALID_HANDLE;
    
    return Plugin_Stop;
}

public Adren_StopAdrenaline(client)
{
    if(Util_IsClientInGame(client))
        if(GetClientTeam(client)!=g_adrenUpgradeConfigCache[UpgradeConfig_TeamLock])
            OffsLib_SetClientLMV(client, GetModifiedSpeed(client));
            
    if(g_hPlayerIsAdrenalined[client]!=INVALID_HANDLE)
    {
        KillTimer(g_hPlayerIsAdrenalined[client]);
        g_hPlayerIsAdrenalined[client]=INVALID_HANDLE;
    }
}

// **********************************************
//                     Menus
// **********************************************

Adren_AddButtonToRPGMenu(Handle:hMenu)
{
    // Add Adrenaline's button to the menu if possible.
    decl String:info[16];

    new count = MenuLib_GetMenuBtnCount(MenuLib_FindMenuById("RPGMenuUpgrades"));
    for (new i = 0; i < count; i++)
    {
        MenuLib_BtnReadString(hMenu, i, MenuBtn_Info, info, sizeof(info));
        if (StrEqual(info, "AdrenBtn"))
            return;
    }
    g_iAdrenRPGMenuButton = MenuLib_AddMenuBtnEx(hMenu, "Adren fullname", "AdrenBtn", true, ITEMDRAW_DEFAULT, INVALID_FUNCTION, BtnNextMenu_LinkMenu, "AdrenMenu");
}

/**
 * Menu callback
 * Called when a certain button in a menu is pressed.  The menu action is always MenuAction_Select.
 *
 * @param menu      The menulib menu handle.
 * @param client    The client index.
 * @param slot      The menu slot selected. (starting from 0)
 */
public Adren_AdrenMenuPurchaseBtn(Handle:hMenu, client, slot)
{
    PurchaseUpgrade(client,g_moduleAdren);
}

/**
 * Menu callback
 * Called when a certain button in a menu is pressed.  The menu action is always MenuAction_Select.
 *
 * @param menu      The menulib menu handle.
 * @param client    The client index.
 * @param slot      The menu slot selected. (starting from 0)
 */
public Adren_AdrenMenuSellBtn(Handle:hMenu, client, slot)
{
    SellUpgrade(client,g_moduleAdren);
}
